#include "Plugin.h"
#include "Scott.h"

Plugin::Plugin(string pluginName)
{
	this->pluginName = pluginName;

	// Get a reference to the DescriptorsRepository
	this->descriptorsRepository = DescriptorsRepository::GetInstance();
}

void Plugin::SetEnumOutput( SEnumTypeDescriptorSp td, string name, unsigned long val, string alternative)
{
	if ( td->IsDefined(val) )
	{
		outputs->Set(name, SEnum::Create(td, val) );
	}
	else
	{
		outputs->Set(name, SEnum::Create(td, alternative) );
	}
}

void Plugin::InitializePlugin(PluginSpMapSp plugins, INameResolutorSp nameResolutor)
{
	// Set references
	this->plugins = plugins;
	this->nameResolutor = nameResolutor;

	vars = SValueSpMapSp( new SValueSpMap() );

	// The default behavior is to build the plugin from the xml plugin descriptor
	LoadScottPluginDescriptor(plugins);
}

std::string Plugin::GetAttribute( TiXmlElement* elem, string attributeName )
{
	const string* pstr = elem->Attribute( attributeName );
	string str = "";

	if ( pstr )
	{
		str = string( *pstr );
	}

	trim(str);

	return str;
}

void Plugin::LoadScottPluginDescriptor(PluginSpMapSp plugins)
{
	bool ris;

	string fileDescriptorName = this->GetName() + ".spd";

	ostringstream os;
	os << "Error in " << fileDescriptorName << endl;

	// Load the scott file descriptor

	TiXmlDocument doc( PLUGIN_DIR + fileDescriptorName);
	ris = doc.LoadFile();
	if ( !ris )
	{
		if ( doc.ErrorId() == TiXmlBase::TIXML_ERROR_OPENING_FILE )
		{
			throw SPluginException("Error: The file descriptor " + fileDescriptorName + " not exists.");
		}

		os << doc.ErrorDesc() << endl;
		os << "Row: " << doc.ErrorRow() << ", Col: " << doc.ErrorCol() << endl;

		throw SPluginException( os.str() );
	}

	// Get the root element

	TiXmlHandle hDoc(&doc);
	TiXmlHandle hRoot(0);


	TiXmlElement* pluginElem = hDoc.FirstChildElement("Plugin").Element();
	if ( !pluginElem )
	{
		os << "The root tag <Plugin> not exists.";
		throw SPluginException( os.str() );
	}

	hRoot = TiXmlHandle(pluginElem);

	// Control if this plugin depends from others plugin
	TiXmlElement* dependencyElem = hRoot.FirstChild("Dependencies").FirstChild("Dependency").Element();	

	for( ; dependencyElem; dependencyElem = dependencyElem->NextSiblingElement() )
	{
		string dependentPluginName = GetAttribute( dependencyElem, "plugin");

		trim(dependentPluginName);

		// Load the plugin dependent
		// Check if the plugin is just loaded
		PluginSpMap::iterator it = plugins->find(dependentPluginName);

		if ( it == plugins->end() )  // the plugin is not loaded
		{
			try
			{
				// Load the plugin
				ScottSp scott( new Scott() );
				PluginSp plugin = scott->CreatePlugin(dependentPluginName, plugins, nameResolutor);

				// Add the loaded plugin in the plugins map
				(*plugins)[dependentPluginName] = plugin;
				nameResolutor->AddOrSelectPlugin(plugin);

				// TODO: Not correct show output here
				cout << "Autoload of '" << dependentPluginName << "'." << endl;
			}
			catch(const SException& ex)
			{
				cout << ex.Message() << endl;
			}
		}		
	}	

	// Build the enum types

	TiXmlElement* enumElem = hRoot.FirstChild("Types").FirstChild("Enums").FirstChild("Enum").Element();	

	for( ; enumElem; enumElem = enumElem->NextSiblingElement() )
	{
		long n = 0;
		string enumName = GetAttribute( enumElem, "name");
		string enumDescription = GetAttribute( enumElem, "description");

		try
		{
			SEnumTypeDescriptorSp etd = SEnumTypeDescriptor::Create(pluginName, enumName, enumDescription);

			// Enum entries
			TiXmlElement* entryElem = enumElem->FirstChildElement("Entry");

			for( ; entryElem; entryElem = entryElem->NextSiblingElement() )
			{
				string name = GetAttribute(entryElem, "name");
				string val = GetAttribute(entryElem, "value");
				string description = GetAttribute(entryElem, "description");

				try	
				{ 
					SNumberSp num = SNumber::Parse(val);
					n = num->Get();
				}
				catch(...)
				{
					++n;
				}

				etd->AddEntry(name, n, description);
			}

			descriptorsRepository->AddTypeDescriptor(etd);
		}
		catch(...)
		{
			os << "Syntax error in <Enum name=\"" << enumName << "\" ... >" << endl;
			throw SPluginException( os.str() );
		}
	}

	// Build the struct and the vector types
	// Note: the for loop is important if there are nested types

	for(int i=0; i<10; i++)
	{
		// Build the vector types
		TiXmlElement* vectorElem = hRoot.FirstChild("Types").FirstChild("Vectors").FirstChild("Vector").Element();	
	
		for( ; vectorElem; vectorElem = vectorElem->NextSiblingElement() )
		{
			string vectorName = GetAttribute( vectorElem, "name");
			string vectorItemType = GetAttribute( vectorElem, "itemType");
			string vectorDescription = GetAttribute( vectorElem, "description");

			try
			{
				try
				{
					// Item type is a simple type
					SType typeName = Utility::GetTypeFromName(vectorItemType);
					SVectorTypeDescriptorSp vtd = SVectorTypeDescriptor::Create(pluginName, vectorName, vectorDescription, typeName);

					descriptorsRepository->AddTypeDescriptor(vtd);
				}
				catch(...)
				{
					// Item type is a user defined type
					try
					{						
						TypeDescriptorSp td = descriptorsRepository->GetTypeDescriptor( nameResolutor->ResolveTypeName(vectorItemType) );
						SVectorTypeDescriptorSp vtd = SVectorTypeDescriptor::Create(pluginName, vectorName, vectorDescription, td);

						descriptorsRepository->AddTypeDescriptor(vtd);
					}
					catch(...) { }
				}
			}
			catch(...)
			{
				os << "Syntax error in <Vector name=\"" << vectorName << "\" ... >" << endl;
				throw SPluginException( os.str() );
			}

		}

		// Build the struct types

		TiXmlElement* structElem = hRoot.FirstChild("Types").FirstChild("Structs").FirstChild("Struct").Element();	

		for( ; structElem; structElem = structElem->NextSiblingElement() )
		{
			string structName = GetAttribute( structElem, "name");
			string structDescription = GetAttribute( structElem, "description");

			try
			{
				SStructTypeDescriptorSp std = SStructTypeDescriptor::Create(pluginName, structName, structDescription);

				TiXmlElement* entryElem = structElem->FirstChildElement("Member");

				for( ; entryElem; entryElem = entryElem->NextSiblingElement() )
				{
					string name = GetAttribute(entryElem, "name");
					string type = GetAttribute(entryElem, "type");
					string description = GetAttribute(entryElem, "description");

					try
					{
						// Member type is a simple type
						SType typeName = Utility::GetTypeFromName(type);

						std->AddMember(name, description, typeName);
						descriptorsRepository->AddTypeDescriptor(std);
					}
					catch(...)
					{
						// Member type is a user defined type
						try
						{
							TypeDescriptorSp td = descriptorsRepository->GetTypeDescriptor( nameResolutor->ResolveTypeName(type) );

							std->AddMember(name, description, td);
							descriptorsRepository->AddTypeDescriptor(std);
						}
						catch(...) { }
					}
				}
			}
			catch(...)
			{
				os << "Syntax error in <Struct name=\"" << structName << "\" ... >" << endl;
				throw SPluginException( os.str() );
			}
		}

	}

	// Build the commands

	TiXmlElement* commandElem = hRoot.FirstChild("Commands").FirstChild("Command").Element();

	for( ; commandElem; commandElem = commandElem->NextSiblingElement() )
	{
		TiXmlElement* descriptionElem = NULL;
		TiXmlElement* inputsElem = NULL;
		TiXmlElement* outputsElem = NULL;

		string commandName = "";
		string commandDescription = "";

		try
		{
			// Get command info
			commandName = GetAttribute(commandElem, "name");

			if ( (descriptionElem = commandElem->FirstChildElement("Description")) )
			{
				commandDescription = descriptionElem->GetText();
			}

			CommandDescriptorSp cd = CommandDescriptor::Create(pluginName, commandName, commandDescription);

			// Command Inputs
			if ( (inputsElem = commandElem->FirstChildElement("Inputs")) )
			{
				TiXmlElement* parameterElem = inputsElem->FirstChildElement();

				for( ; parameterElem; parameterElem = parameterElem->NextSiblingElement() )
				{
					string name = GetAttribute(parameterElem, "name");
					string type = GetAttribute(parameterElem, "type");
					string description = GetAttribute(parameterElem, "description");

					try
					{
						// Input parameter type is a simple type
						SType typeName = Utility::GetTypeFromName(type);
						cd->AddInputParameter(name, description, typeName);
					}
					catch(...)
					{
						// Input parameter type is a user defined type
						try
						{
							TypeDescriptorSp td = descriptorsRepository->GetTypeDescriptor( nameResolutor->ResolveTypeName(type) );
							cd->AddInputParameter(name, description, td);
						}
						catch(...) { }
					}
				}
			}

			// Command Outputs

			if ( (outputsElem = commandElem->FirstChildElement("Outputs")) )
			{
				TiXmlElement* parameterElem = outputsElem->FirstChildElement();

				for( ; parameterElem; parameterElem = parameterElem->NextSiblingElement() )
				{
					string name = GetAttribute(parameterElem, "name");;
					string type = GetAttribute(parameterElem, "type");
					string description = GetAttribute(parameterElem, "description");

					try
					{
						// Output parameter type is a simple type
						SType typeName = Utility::GetTypeFromName(type);
						cd->AddOutputParameter(name, description, typeName);
					}
					catch(...)
					{
						// Output parameter type is a user defined type
						try
						{
							TypeDescriptorSp td = descriptorsRepository->GetTypeDescriptor( nameResolutor->ResolveTypeName(type) );
							cd->AddOutputParameter(name, description, td);
						}
						catch(...) { }
					}				
				}
			}

			// Add the command descriptor in the repository
			descriptorsRepository->AddCommandDescriptor(cd);
		}
		catch(...)
		{
			os << "Syntax error in <Command name=\"" << commandName << "\" ... >" << endl;
			throw SPluginException( os.str() );
		}
	}

	return;
}

void Plugin::ExecuteCommand( CommandSp cmd )
{
	this->cmd = cmd;
	commandName = cmd->GetCommandName();
	inputs = cmd->GetInputs();
	outputs = cmd->GetOutputs();
}

void Plugin::PushCommandStatus()
{
	commandStack.push(cmd);
}

void Plugin::PopCommandStatus()
{
	if ( !commandStack.empty() )
	{
		cmd = commandStack.top();
		commandStack.pop();

		// Restore the command status
		commandName = cmd->GetCommandName();
		inputs = cmd->GetInputs();
		outputs = cmd->GetOutputs();
	}
}